package org.hjj.design.auth.domain.token;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;

/**
 * handle token
 *
 * @author:何嘉俊
 * @date:2021/5/7
 */
public class AuthToken {
    private static final long DEFAULT_EXPIRED_TIME_INTERVAL = 1 * 60 * 1000;

    private String token;

    private long createTime;

    /**
     * Default expired time is one minute, but u can change it through constructor.
     */
    private long expiredTimeInterval = DEFAULT_EXPIRED_TIME_INTERVAL;

    public AuthToken(String token, long createTime) {
        this.token = token;
        this.createTime = createTime;
    }

    public AuthToken(String token, long createTime, long expiredTimeInterval) {
        this(token, createTime);
        this.expiredTimeInterval = expiredTimeInterval;
    }

    /**
     * generate a new token
     * @param baseUrl
     * @param appId
     * @param password
     * @param createTime
     * @return
     */
    public static AuthToken generate(String baseUrl, String appId, String password, long createTime) {
        String value = baseUrl + "?appId=" + appId + "&timestamp=" + createTime;
        String token = hmacSha1(value, password);
        return new AuthToken(token, createTime);
    }

    /**
     * generate token by HmacSHA1 algorithm
     * @param value
     * @param key
     * @return
     */
    private static String hmacSha1(String value, String key) {
        try {
            // Get an hmac_sha1 key from the raw key bytes
            byte[] keyBytes = key.getBytes();
            SecretKeySpec signingKey = new SecretKeySpec(keyBytes, "HmacSHA1");

            // Get an hmac_sha1 Mac instance and initialize with the signing key
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signingKey);

            // Compute the hmac on input data bytes
            byte[] rawHmac = mac.doFinal(value.getBytes());

            byte[] result = Base64.getEncoder().encode(rawHmac);

            //  Covert array of Hex bytes to a String
            return new String(result, "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public String getToken() {
        return token;
    }

    /**
     * determine whether token is expired or not
     * @return true: expired, false: not expired
     */
    public boolean isExpired() {
        if (this.createTime > new Date().getTime() + expiredTimeInterval) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * determine whether token is the same as determine
     * @param authToken
     * @return true: same, false: not same
     */
    public boolean match(AuthToken authToken) {
        return this.token.equals(authToken.token) ? true : false;
    }
}
